#include <http.h>
#include "FunTrace.h"
#include "ApplicationUpdater.h"
#include "CharUtils.h"
#include <aknmessagequerydialog.h> //CAknMessageQueryDialog
#include <aknnotewrappers.h> //CAknInformationNote
#include <aknglobalnote.h> 

static void PrintNoteL(TRefByValue<const TDesC> aFormat, ...)
    {
    VA_LIST list;
    VA_START(list, aFormat);
    TBuf<256> buf;
    buf.FormatList(aFormat, list);
    VA_END(list);
	CAknGlobalNote* globalNote = CAknGlobalNote::NewLC();
	globalNote->ShowNoteL(EAknGlobalInformationNote, buf); 
    CleanupStack::PopAndDestroy(globalNote);
    }

void CApplicationUpdater::SetApplicationInformationL(const TDesC& aApplicationName, const TDesC8& aApplicationVersion, const TDesC8& aManifestURL, const TDesC& aDownloadPath, TInt aWaitNoteResource, TInt aInstallQueryResource, TInt aProgressNoteResource)
	{
	TFunTrace funTrace(__PRETTY_FUNCTION__);
	delete iApplicationName;
	delete iApplicationVersion;
	delete iManifestURL;
	delete iDownloadPath;
	iApplicationName = aApplicationName.AllocL();
	iApplicationVersion = aApplicationVersion.AllocL();
	iManifestURL = aManifestURL.AllocL();
	iDownloadPath = aDownloadPath.AllocL();
	iWaitNoteResource = aWaitNoteResource;
	iInstallQueryResource = aInstallQueryResource;
	iProgressNoteResource = aProgressNoteResource;
	}

void CApplicationUpdater::CloseConnectionL()
	{
	TFunTrace funTrace(__PRETTY_FUNCTION__);
	Cancel();
	iSession.Close();
	}

void CApplicationUpdater::SetConnectionInfoL(const RConnection& aConnection, const RSocketServ& aSocketServ)
	{
	TFunTrace funTrace(__PRETTY_FUNCTION__);
	iSession.OpenL();
	RStringPool strP = iSession.StringPool();
	RHTTPConnectionInfo connInfo = iSession.ConnectionInfo();
	connInfo.SetPropertyL ( strP.StringF(HTTP::EHttpSocketServ, RHTTPSession::GetTable() ), THTTPHdrVal (aSocketServ.Handle()) );
	TInt connPtr = REINTERPRET_CAST(TInt, &aConnection);
	connInfo.SetPropertyL ( strP.StringF(HTTP::EHttpSocketConnection, RHTTPSession::GetTable() ), THTTPHdrVal (connPtr) );
	}

void CApplicationUpdater::Cancel()
	{
	TFunTrace funTrace(__PRETTY_FUNCTION__);
	if(iDownloadWaitDialog)
		{
		iDownloadWaitDialog->ProcessFinishedL();
		iDownloadWaitDialog = NULL; // deleted by ProcessFinishedL
		}
	if(iManifestTransaction != RHTTPTransaction())
		{
    	iManifestTransaction.Cancel();
    	iManifestTransaction.Close();
    	iManifestTransaction = RHTTPTransaction();
		}
	iSisDownloadFile.Close();
	if(iSisDownloadTransaction != RHTTPTransaction())
		{
		iSisDownloadTransaction.Cancel();
		iSisDownloadTransaction.Close();
		iSisDownloadTransaction = RHTTPTransaction();
		}
	iFs.Delete(*iDownloadPath);
	if(iDownloadProgressDialog)
		{
		iDownloadProgressDialog->ProcessFinishedL();
		iDownloadProgressDialog = NULL; // deleted by ProcessFinishedL
		}
	iDownloadPhase = IDLE;
	}

CApplicationUpdater* CApplicationUpdater::NewLC()
	{
	TFunTrace funTrace(__PRETTY_FUNCTION__);
	CApplicationUpdater* ret = new (ELeave) CApplicationUpdater;
	CleanupStack::PushL(ret);
	ret->ConstructL();
	return ret;
	}

TBool CApplicationUpdater::UpdatePending() const
{
	return (iDownloadPhase != IDLE);
}

CApplicationUpdater* CApplicationUpdater::NewL()
	{
	TFunTrace funTrace(__PRETTY_FUNCTION__);
	CApplicationUpdater* ret = CApplicationUpdater::NewLC();
	CleanupStack::Pop(ret);	
	return ret;
	}

const TDesC& CApplicationUpdater::DownloadPath() const
{
TFunTrace funTrace(__PRETTY_FUNCTION__);
	return *iDownloadPath;
}

CApplicationUpdater::~CApplicationUpdater()
	{
	TFunTrace funTrace(__PRETTY_FUNCTION__);
	Cancel();
	delete iDownloadWaitDialog;
	delete iDownloadProgressDialog;
	delete iExitTimerHelper;
	iExitTimer.Close();
	iSisDownloadTransaction.Close();
	iManifestTransaction.Close();
	iManifest.Close();
	iSisDownloadFile.Close();
	iApaLsSession.Close();
	delete iApplicationName;
	delete iApplicationVersion;
	delete iManifestURL;
	delete iDownloadPath;	
	iFs.Close();
	iSession.Close();
	}

CApplicationUpdater::CApplicationUpdater()
	{
	TFunTrace funTrace(__PRETTY_FUNCTION__);
	iDownloadProgressDialog = NULL;
	iDownloadWaitDialog = NULL;
	iExitTimerHelper = NULL;	
	iApplicationName = NULL;
	iApplicationVersion = NULL;
	iManifestURL = NULL;
	iDownloadPath = NULL;
	iDownloadPhase = IDLE;
	}

void CApplicationUpdater::ConstructL()
	{
	TFunTrace funTrace(__PRETTY_FUNCTION__);
	iManifest.CreateL(0);
	User::LeaveIfError(iApaLsSession.Connect());
	User::LeaveIfError(iExitTimer.CreateLocal());
	iExitTimerHelper = CActiveHelper<RTimer*>::NewL();
	User::LeaveIfError( iFs.Connect() );
	}

void CApplicationUpdater::StartUpdateSequenceL(TBool aSilentUpdate, MApplicationUpdaterCallback* aTarget)
	{
	TFunTrace funTrace(__PRETTY_FUNCTION__);
	iTarget = aTarget;
	iSilentUpdate = aSilentUpdate;
	RBuf8 uristr;
	CleanupClosePushL(uristr);
	TUriParser8 uri;
	uri.Parse( *iManifestURL );
	RStringF method = iSession.StringPool().StringF(HTTP::EGET,RHTTPSession::GetTable());
	CleanupClosePushL(method);
	if(iManifestTransaction != RHTTPTransaction())
		{
		iManifestTransaction.Cancel();
		iManifestTransaction.Close();
		}
	iManifestTransaction = iSession.OpenTransactionL(uri, *this, method);
	iManifest.Zero();
	iLastError = KErrNone;
	iManifestTransaction.SubmitL();
	if(!iSilentUpdate)
		{
		delete iDownloadWaitDialog;
		iDownloadWaitDialog = new (ELeave) CAknWaitDialog((REINTERPRET_CAST(CEikDialog**, &iDownloadWaitDialog)), ETrue);
		iDownloadWaitDialog->PrepareLC(iWaitNoteResource);
		iDownloadWaitDialog->SetCallback(this);
		iDownloadWaitDialog->RunLD();
		}
	iDownloadPhase = DOWNLOAD_PHASE_MANIFEST;
	CleanupStack::PopAndDestroy(&method);
	CleanupStack::PopAndDestroy(&uristr);
	}

static TPtrC8 GetToken(TPtrC8& aBuf, TChar aChar)
	{
	TFunTrace funTrace(__PRETTY_FUNCTION__);
	TInt ret = aBuf.Locate(aChar);
	TPtrC8 token;
	if(ret != KErrNotFound)
		{
		token.Set( aBuf.Left(ret) );
		aBuf.Set( aBuf.Mid(ret + 1) );
		}
	else
		{
		token.Set( aBuf );
		aBuf.Set( aBuf.Mid(aBuf.Length()) );		
		}
	return token;
	}

static TInt OpenURLL(const TDesC& aUrl)
	{
	RApaLsSession apaLsSession;
	const TUid KOSSBrowserUidValue = {0x10008D39}; // 0x1020724D for S60 3rd Ed
	RBuf param;
	CleanupClosePushL(param);
	param.CreateL(128);
	param.Format(_L("4 %S"), &aUrl);
	TUid id(KOSSBrowserUidValue);
	TApaTaskList taskList(CEikonEnv::Static()->WsSession());
	TApaTask task = taskList.FindApp(id);
	
	TInt ret = KErrNone;
	
	if(task.Exists())
	    {
		RProcess proc;
		if(proc.HasCapability(ECapabilitySwEvent, NULL))
			{
			task.BringToForeground();
			RBuf8 param8;
			CleanupClosePushL(param8);
			param8.CreateL(param.Length());
			param8.Copy(param);
		    task.SendMessage(TUid::Uid(0), param8); // UID not used
			CleanupStack::PopAndDestroy(&param8);
			}
		else
			{
			CAknErrorNote* msg = new (ELeave) CAknErrorNote(EFalse);
			msg->ExecuteLD(_L("Please close the Web browser and try again"));
			ret = KErrAccessDenied;
			}
	    }
	else
	    {
	    if(!apaLsSession.Handle())
	      {
	      User::LeaveIfError(apaLsSession.Connect());
	      }
	    TThreadId thread;
	    User::LeaveIfError(apaLsSession.StartDocument(param, KOSSBrowserUidValue, thread));
	    apaLsSession.Close();   
	    }
	CleanupStack::PopAndDestroy(&param);
	return ret;
	} 


void CApplicationUpdater::MHFRunL(RHTTPTransaction aTransaction, const THTTPEvent& aEvent)
	{
	
	TFunTrace funTrace(__PRETTY_FUNCTION__);
	if(aEvent.iStatus < KErrNone) iLastError = aEvent.iStatus;
	if(aTransaction == iManifestTransaction)
		{
		  switch (aEvent.iStatus)
		    {
				case THTTPEvent::EUnrecoverableError:
					iLastError = KErrNotReady; // for Symbian 5th ed + 
				  break;
		    case THTTPEvent::EGotResponseBodyData:
		    	{
			      MHTTPDataSupplier* body = aTransaction.Response().Body();
			      TPtrC8 part;	      
			      body->GetNextDataPart(part);
			      if(part.Length() + iManifest.Length() > iManifest.MaxLength() )
			    	  iManifest.ReAllocL(part.Length() + iManifest.Length());	    	  	    	  
			      iManifest.Append(part);	    	  	    	  
			      body->ReleaseData();
		    	}		      
		      break;
		      
		    case THTTPEvent::EFailed:
		    case THTTPEvent::ESucceeded:
		    	{
		    	iManifestTransaction = RHTTPTransaction();
		    	iStatusCode = aTransaction.Response().StatusCode();
		    	if(aEvent.iStatus != THTTPEvent::ESucceeded || iStatusCode != 200)
		    		User::Leave(iLastError < 0 ? iLastError : KErrAbort);
		    	if(iDownloadWaitDialog) iDownloadWaitDialog->ProcessFinishedL();
		    	
		    	if(aEvent.iStatus == THTTPEvent::ESucceeded)
		    		{
			    	TPtrC8 version, url, comment, stream = iManifest;
			    	version.Set ( GetToken(stream, '\n') );
			    	url.Set ( GetToken(stream, '\n') );
			    	comment.Set ( stream );
			    	
			    	iTarget->MApplicationUpdaterCallbackRunL(EAUE_MANIFEST_DOWNLOADED);

			    	TBool browserMode = (url.Length() > 5 && url.Right(5).MatchF(_L8(".SISX")) == KErrNotFound);
			    	
			    	if(version != *iApplicationVersion)
			    		{
			    	
			    		
			    		CAknMessageQueryDialog* queryDialog = NULL;
						{
						RBuf buf;
						buf.CreateL(1024);
						CleanupClosePushL(buf);
						buf.Format(_L("%S %S\n%S"), iApplicationName, narrowToWideLC(version), narrowToWideLC(comment));
						CleanupStack::PopAndDestroy(2); // narrowToWideLC
						queryDialog = CAknMessageQueryDialog::NewL( buf );
						CleanupStack::PopAndDestroy(&buf);
						if(browserMode)
							queryDialog->SetHeaderText(_L("Go to download page?"));
						else
							queryDialog->SetHeaderText(_L("Install new version?"));
						}
						if(queryDialog->ExecuteLD( iInstallQueryResource ))
			    		{
			    		
							if(browserMode)
								{
								HBufC* urlWide = narrowToWideLC(url);
								if(OpenURLL(*urlWide) == KErrNone)
									{
							    	iExitTimerHelper->Cancel();
							    	iExitTimer.After(iExitTimerHelper->iStatus, 0);
							    	iExitTimerHelper->SetActive(this, &iExitTimer);
									}
								CleanupStack::PopAndDestroy(urlWide);
								}
							else
								{
								delete iDownloadProgressDialog;
								iDownloadProgressDialog = new (ELeave) CAknProgressDialog((REINTERPRET_CAST(CEikDialog**, &iDownloadProgressDialog)), ETrue);
								iDownloadProgressDialog->PrepareLC(iProgressNoteResource);
								iDownloadProgressDialog->SetCallback(this);
								iDownloadProgressDialog->RunLD();
								
								RBuf8 uristr;
								CleanupClosePushL(uristr);
								TUriParser8 uri;
								uri.Parse( url );
								iSisDownloadFile.Close();
								User::LeaveIfError(iSisDownloadFile.Replace(iFs, *iDownloadPath, EFileWrite));
								RStringF method = iSession.StringPool().StringF(HTTP::EGET,RHTTPSession::GetTable());
								CleanupClosePushL(method);
								if(iSisDownloadTransaction != RHTTPTransaction())
									{
									iSisDownloadTransaction.Cancel();
									iSisDownloadTransaction.Close();
									}
								iSisDownloadTransaction = iSession.OpenTransactionL(uri, *this, method);
								iLastError = KErrNone;
								iSisDownloadTransaction.SubmitL();
								iDownloadPhase = DOWNLOAD_PHASE_SIS;
								CleanupStack::PopAndDestroy(&method);
								CleanupStack::PopAndDestroy(&uristr);
								}
			    		}
			    		}
			    	else
			    		{
			    		if(!iSilentUpdate)
			    			{
							CAknInformationNote* note = new ( ELeave ) CAknInformationNote();
							note->ExecuteLD( _L("No new version available"));
			    			}
			    		iDownloadPhase = IDLE;
			    		}
		    		}
		    	aTransaction.Cancel();
		    	aTransaction.Close();
		    	}
		      break;

		    case THTTPEvent::ERedirectedPermanently:
		      break;
		    	
		    case THTTPEvent::ERedirectedTemporarily:
		      break;
		    	
		    default:
		      break;
		    }		
		}
	else if(aTransaction == iSisDownloadTransaction)
		{
		  switch (aEvent.iStatus)
		    {
				case THTTPEvent::EUnrecoverableError:
					iLastError = KErrNotReady; // for Symbian 5th ed + 
				  break;
			case THTTPEvent::EGotResponseHeaders:
				{
				RHTTPHeaders hdr = aTransaction.Response().GetHeaderCollection();
				THTTPHdrVal valContentLength;
				hdr.GetField(
						iSession.StringPool().StringF(HTTP::EContentLength, RHTTPSession::GetTable()), 0,
						valContentLength);
	    		CEikProgressInfo* downloadProgressInfo = iDownloadProgressDialog->GetProgressInfoL();
	    		downloadProgressInfo->SetFinalValue(valContentLength);
				break;
				}
		    case THTTPEvent::EGotResponseBodyData:
		    	{
			      MHTTPDataSupplier* body = aTransaction.Response().Body();
			      TPtrC8 part;	      
			      body->GetNextDataPart(part);
			      User::LeaveIfError(iSisDownloadFile.Write(part));
			      body->ReleaseData();
			      TInt size;
			      User::LeaveIfError(iSisDownloadFile.Size(size));
			      CEikProgressInfo* downloadProgressInfo = iDownloadProgressDialog->GetProgressInfoL();
			      downloadProgressInfo->SetAndDraw(size);
		    	}		      
		      break;
		      
		    case THTTPEvent::EFailed:
		    case THTTPEvent::ESucceeded:
		    	{
		    	iSisDownloadTransaction = RHTTPTransaction();
		    	iStatusCode = aTransaction.Response().StatusCode();
		    	if(aEvent.iStatus != THTTPEvent::ESucceeded || iStatusCode != 200)
		    		User::Leave(iLastError < 0 ? iLastError : KErrAbort);
		    	iDownloadPhase = IDLE;
		    	iStatusCode = aTransaction.Response().StatusCode();
		    	iSisDownloadFile.Close();
		    	iDownloadProgressDialog->ProcessFinishedL();
		    	TThreadId threadId;
		    	User::LeaveIfError(iApaLsSession.StartDocument(*iDownloadPath, threadId));
		    	iExitTimerHelper->Cancel();
		    	iExitTimer.After(iExitTimerHelper->iStatus, 0);
		    	iExitTimerHelper->SetActive(this, &iExitTimer);
		    	aTransaction.Cancel();
		    	aTransaction.Close();
		    	}
		      break;

		    case THTTPEvent::ERedirectedPermanently:
		      break;
		    	
		    case THTTPEvent::ERedirectedTemporarily:
		      break;
		    	
		    default:
		      break;
		    }
		}
	}
void CApplicationUpdater::MActiveHelperCallbackRunL(CActiveHelper<RTimer*>* aActiveHelper, RTimer* aTimer)
	{
	TFunTrace funTrace(__PRETTY_FUNCTION__);
	if(aTimer == &iExitTimer)
		iTarget->MApplicationUpdaterCallbackRunL(EAUE_EXIT);
	}
TInt CApplicationUpdater::MActiveHelperCallbackRunError(CActiveHelper<RTimer*>* aActiveHelper, RTimer* aTimer, TInt aError)
	{
	TFunTrace funTrace(__PRETTY_FUNCTION__);
	if(aTimer == &iExitTimer)
		return KLeaveExit;
	}
void CApplicationUpdater::MActiveHelperCallbackDoCancel(CActiveHelper<RTimer*>* aActiveHelper, RTimer* aTimer)
	{
	TFunTrace funTrace(__PRETTY_FUNCTION__);
	aTimer->Cancel();
	}

TInt CApplicationUpdater::MHFRunError(TInt aError, RHTTPTransaction aTransaction, const THTTPEvent& aEvent)
	{
	TFunTrace funTrace(__PRETTY_FUNCTION__);
	CAknErrorNote* note = new ( ELeave ) CAknErrorNote();
	TBuf<256> errorMessage;
	errorMessage.Format( _L("Upgrade failed (error %d, %d).\nPlease try again later or upgrade via the homepage"), aError, iStatusCode);
	note->ExecuteLD( errorMessage );	
	Cancel();
	if(aError == KErrNotReady || aError == KErrConnectionTerminated) // not connected or connection lost
		iTarget->MApplicationUpdaterCallbackRunL(EAUE_CONNECTION_ERROR);	
	return KErrNone;
	}
void CApplicationUpdater::DialogDismissedL( TInt aButtonId )
	{
	TFunTrace funTrace(__PRETTY_FUNCTION__);
	if (aButtonId == EAknSoftkeyCancel)
		{
		switch(iDownloadPhase)
			{
			case DOWNLOAD_PHASE_MANIFEST:
				iDownloadWaitDialog = NULL; // the dialog is already deleted
			break;
			case DOWNLOAD_PHASE_SIS:
				iDownloadProgressDialog = NULL; // the dialog is already deleted
				break;
			}
		Cancel();
		}
	}

